
#include <BGroup>
#include <BShape>
#include "member_BShape.h"

using namespace BWE;

#define member					(*(member_BShape*)_ptr)
#define member_allocate()		_ptr = new member_BShape(this)
#define member_release()		delete (member_BShape*)_ptr

BShape::BShape(const BString& name) : BObject(name)
{
	member_allocate();
}
BShape::BShape(const BShape& other) : BObject(other)
{
	member_allocate();
	this->copy(other);
}
BShape::~BShape()
{
	member_release();
}

void BShape::copy(const BShape& other)
{
	member.visible = shape_member(&other)->visible;
	member.collible = shape_member(&other)->collible;
	member.matrix = shape_member(&other)->matrix;
	member.lighting = shape_member(&other)->lighting;
	member.depthTest = shape_member(&other)->depthTest;
	member.cullback = shape_member(&other)->cullback;
	member.pointSmooth = shape_member(&other)->pointSmooth;
	member.lineSmooth = shape_member(&other)->lineSmooth;
	member.polygonSmooth = shape_member(&other)->polygonSmooth;
	member.lineWidth = shape_member(&other)->lineWidth;
	member.pointSize = shape_member(&other)->pointSize;
	member.transparent = shape_member(&other)->transparent;
	member.polyMode = shape_member(&other)->polyMode;
	member.color = shape_member(&other)->color;
	member.material = shape_member(&other)->material;
	member.textures = shape_member(&other)->textures;
	member.program = shape_member(&other)->program;
}

void BShape::setGroup(BGroup* group)
{
	if (group && member.group != group)
	{
		group->insert(this);
	}
}
BGroup* BShape::group() const
{
	return member.group;
}

void BShape::setVisible(bool visible)
{
	if (member.visible != visible)
	{
		member.visible = visible;
		if (visible)
			emit(Signal_Show);
		else
			emit(Signal_Hide);
		emit(Signal_Changed);
		emit(Signal_Fresh);
	}
}
bool BShape::visible() const
{
	return member.visible;
}

void BShape::setCollible(bool collible)
{
	if (member.collible)
	{
		member.collible = collible;
		emit(Signal_Changed);
	}
}
bool BShape::collible() const
{
	return member.collible;
}

void BShape::setCullback(bool cullback)
{
	if (member.cullback != cullback)
	{
		member.cullback = cullback;
		emit(Signal_Changed);
		emit(Signal_Fresh);
	}
}
bool BShape::cullback() const
{
	return member.cullback;
}

void BShape::setLighting(bool lighting)
{
	if (member.lighting != lighting)
	{
		member.lighting = lighting;
		emit(Signal_Changed);
		emit(Signal_Fresh);
	}
}
bool BShape::lighting() const
{
	return member.lighting;
}

void BShape::setDepthTest(bool depthTest)
{
	if (member.depthTest != depthTest)
	{
		member.depthTest = depthTest;
		emit(Signal_Changed);
		emit(Signal_Fresh);
	}
}
bool BShape::depthTest() const
{
	return member.depthTest;
}

void BShape::setLineSmooth(bool lineSmooth)
{
	if (member.lineSmooth != lineSmooth)
	{
		member.lineSmooth = lineSmooth;
		emit(Signal_Changed);
		emit(Signal_Fresh);
	}
}
bool BShape::lineSmooth() const
{
	return member.lineSmooth;
}

void BShape::setPolygonSmooth(bool polygonSmooth)
{
	if (member.polygonSmooth != polygonSmooth)
	{
		member.polygonSmooth = polygonSmooth;
		emit(Signal_Changed);
		emit(Signal_Fresh);
	}
}
bool BShape::polygonSmooth() const
{
	return member.polygonSmooth;
}

void BShape::setLineWidth(int lineWidth)
{
	if (lineWidth <= 0)
		lineWidth = 1;
	if (member.lineWidth != lineWidth)
	{
		member.lineWidth = lineWidth;
		emit(Signal_Changed);
		emit(Signal_Fresh);
	}
}
int BShape::lineWidth() const
{
	return member.lineWidth;
}

void BShape::setPointSmooth(bool pointSmooth)
{
	if (member.pointSmooth != pointSmooth)
	{
		member.pointSmooth = pointSmooth;
		emit(Signal_Changed);
		emit(Signal_Fresh);
	}
}
bool BShape::pointSmooth() const
{
	return member.pointSmooth;
}

void BShape::setPointSize(int pointSize)
{
	if (pointSize <= 0)
		pointSize = 1;
	if (member.pointSize != pointSize)
	{
		member.pointSize = pointSize;
		emit(Signal_Changed);
		emit(Signal_Fresh);
	}
}
int BShape::pointSize() const
{
	return member.pointSize;
}

void BShape::setTransparent(bool transparent)
{
	if (member.transparent != transparent)
	{
		member.transparent = transparent;
		emit(Signal_Changed);
		emit(Signal_Fresh);
	}
}
bool BShape::transparent() const
{
	return member.transparent;
}

void BShape::setPolyMode(PolyMode polyMode)
{
	if (member.polyMode != polyMode)
	{
		member.polyMode = polyMode;
		emit(Signal_Changed);
		emit(Signal_Fresh);
	}
}
PolyMode BShape::polyMode() const
{
	return (PolyMode)member.polyMode;
}

void BShape::setColor(BByte r, BByte g, BByte b, BByte a)
{
	setColor(BColor(r, g, b, a));
}
void BShape::setColor(const BColor& color)
{
	if (member.color != color)
	{
		member.color = color;
		emit(Signal_Fresh);
	}
}
const BColor& BShape::color() const
{
	return member.color;
}

void BShape::setMaterial(BMaterial* material)
{
	if (member.material != material)
	{
		if (member.material)
			member.disconnect(member.material);
		member.material = material;
		if (member.material)
			member.connect(material, Signal_Changed, &member_BShape::slotMaterialChanged);
		emit(Signal_Changed, member.material);
		emit(Signal_Fresh);
	}
}
BMaterial* BShape::material() const
{
	return member.material.ptr();
}

bool BShape::setTexture(BTexture* texture, int index)
{
	if (index < 0 || index > 100)
		return false;
	if (member.textures.size() <= index)
		member.textures.resize(index + 1);
	if (member.textures[index] != texture)
	{
		member.disconnect(member.textures[index]);
		member.connect(texture, Signal_Dirty, &member_BShape::slotTextureDirty);
		member.textures[index] = texture;
		for (int i = member.textures.size(); i > 0; i--)
		{
			if (member.textures[i - 1])
			{
				member.textures.remove(i, member.textures.size());
				break;
			}
		}
		emit(Signal_Changed, member.material);
		emit(Signal_Fresh);
	}
	return true;
}
BTexture* BShape::texture(int index) const
{
	return member.textures(index);
}

bool BShape::removeTexture(BTexture* texture)
{
	if (!texture)
		return false;
	for (int i = 0; i < member.textures.size(); i++)
	{
		if (member.textures[i] == texture)
		{
			BHolder<BTexture> holder(texture);
			member.textures[i] = 0;
			for (int i = member.textures.size(); i > 0; i--)
			{
				if (member.textures[i - 1])
				{
					member.textures.remove(i, member.textures.size());
					break;
				}
			}
			emit(Signal_Changed, texture);
			emit(Signal_Fresh);
			return true;
		}
	}
	return false;
}
int BShape::textureCount() const
{
	return member.textures.size();
}

void BShape::setProgram(BProgram* program)
{
	if (member.program != program)
	{
		if (member.program)
			member.disconnect(member.program);
		member.program = program;
		if (member.program)
			member.connect(member.program, Signal_Dirty, &member_BShape::slotProgramDirty);
		emit(Signal_Changed, member.program);
		emit(Signal_Fresh);
	}
}
BProgram* BShape::program() const
{
	return member.program.ptr();
}

void BShape::setMatrix(const BMatrix& matrix)
{
	if (member.matrix != matrix)
	{
		member.matrix = matrix;
		member.dirtySpace = true;
		emit(Signal_Transformed);
		emit(Signal_Fresh);
	}
}
const BMatrix& BShape::matrix() const
{
	return member.matrix;
}

void BShape::move(const BVector& move)
{
	BMatrix matrix = member.matrix;
	matrix.setPosition(matrix.position() + move);
	setMatrix(matrix);
}
void BShape::move(BReal x, BReal y, BReal z)
{
	BMatrix matrix = member.matrix;
	matrix.setPosition(matrix.position() + BVector(x, y, z));
	setMatrix(matrix);
}

void BShape::setPosition(BReal x, BReal y, BReal z)
{
	BMatrix matrix = member.matrix;
	matrix.setPosition(x, y, z);
	setMatrix(matrix);
}
void BShape::setPosition(const BVector& position)
{
	BMatrix matrix = member.matrix;
	matrix.setPosition(position);
	setMatrix(matrix);
}
BVector BShape::position() const
{
	return member.matrix.position();
}

void BShape::setRotate(BReal rx, BReal ry, BReal rz)
{
	BQuater xrotate(BVector(1, 0, 0), rx);
	BQuater yrotate(BVector(0, 1, 0), ry);
	BQuater zrotate(BVector(0, 0, 1), rz);
	BMatrix matrix = member.matrix;
	matrix.setRotate(xrotate * yrotate * zrotate);
	setMatrix(matrix);
}
void BShape::setRotate(BReal qx, BReal qy, BReal qz, BReal qw)
{
	BMatrix matrix = member.matrix;
	matrix.setRotate(qx, qy, qz, qw);
	setMatrix(matrix);
}
void BShape::setRotate(const BQuater& quater)
{
	BMatrix matrix = member.matrix;
	matrix.setRotate(quater);
	setMatrix(matrix);
}
BQuater BShape::rotate() const
{
	return member.matrix.rotate();
}

void BShape::setScale(BReal x, BReal y, BReal z)
{
	BMatrix matrix = member.matrix;
	matrix.setScale(x, y, z);
	setMatrix(matrix);
}
void BShape::setScale(const BVector& scale)
{
	BMatrix matrix = member.matrix;
	matrix.setScale(scale);
	setMatrix(matrix);
}
BVector BShape::scale() const
{
	return member.matrix.scale();
}

const BBox& BShape::box() const
{
	if (member.dirty)
		((BShape*)this)->flush();
	return member.box;
}
const BSpace& BShape::space() const
{
	if (member.dirtySpace)
	{
		BBox box = this->box() * member.matrix;
		member.space.reset(box.v0());
		member.space.expand(box.v1());
		member.space.expand(box.v2());
		member.space.expand(box.v3());
		member.space.expand(box.v4());
		member.space.expand(box.v5());
		member.space.expand(box.v6());
		member.space.expand(box.v7());
		member.dirtySpace = false;
	}
	return member.space;
}

void BShape::fresh() const
{
	emit(Signal_Fresh);
}
void BShape::dirty() const
{
	member.dirtySpace = true;
	member.dirty = true;
	emit(Signal_Dirty);
}
void BShape::flush() const
{
	if (member.dirty)
	{
		member.box.reset();
		((BShape*)this)->refresh(member.box);
		member.dirty = false;
		member.dirtySpace = true;
		emit(Signal_Flushed);
	}
}

void BShape::clear()
{
	member.program = 0;
	member.material = 0;
	member.textures.clear();
}
